import json
import os
from collections import namedtuple

import stackprinter

from devices.mengy_ks833 import KS833_OutputStatus
from mengy_spac800 import AutoTestUpdateConf

DISCT_RO = (
    (0x0000, 30),  # 实时更新信息类
    (0x0900, 4),  # 事件信息状态寄存器
    (0x0920, 10),  # 最新保护事件寄存器
    (0x0950, 23),  # 历史保护事件寄存器
    (0x0A00, 8),  # 历史遥信事件寄存器
    (0x0E00, 8),  # 装置调试信息寄存器
)

DISCT_RW = (
    # (0x0000, 8),  # 实时更新信息类 时间 远方就地状态
    # (0x0040, 4),  # 实时更新信息类 有功电度
    # (0x0048, 4),  # 实时更新信息类 无功电度
    (0x0200, 10),  # 系统参数寄存器 PT CT
    (0x0300, 26),  # 开入参数寄存器
    (0x0400, 41),  # 出口参数寄存器
    (0x0600, 35),  # 通信参数寄存器
    (0x0700, 8),  # 产品信息寄存器
    (0x0800, 76),  # 保护整定寄存器
    (0x0E40, 32),  # 保护通道校正系数寄存器
)

DISCT_WO = (
    (0x0910, 6),  # 事件信息交互功能寄存器
    (0x0B00, 2),  # 遥控出口寄存器
    (0x0B20, 5),  # 系统控制寄存器
    (0x0E20, 4),  # 调试控制寄存器
    (0x0E70, 8),  # 开出调试寄存器
    (0x0E90, 17),  # 交流量旁路通道控制寄存器
)

TAB_SEND_QUEUE = (
    ((0x0000, 30), (0x0900, 4), (0x0920, 10)),
    ((0x0000, 5), (0x0900, 4), (0x0920, 10), (0x0040, 16), (0x0200, 10), (0x0600, 35), (0x0700, 8), (0x0900, 4)),
    ((0x0900, 4), (0x0920, 10), (0x0300, 26), (0x0400, 41)),
    ((0x0900, 4), (0x0920, 10), (0x040A, 31), (0x0800, 59)),
    ((0x030D, 12), (0x0900, 4), (0x0900, 4), (0x0920, 10)),
    ((0x0900, 4), (0x0920, 10), (0x0E00, 12)),
    ((0x0004, 6), (0x000E, 16), (0x0900, 4), (0x0920, 10), (0x0E40, 32)),
    ((0x0900, 4), (0x0920, 10), (0x000E, 16), (0x0700, 8)),
)


DI_FUNC_TYPE = ("普通开入", "1DL分位", "2DL分位", "3DL分位", "手车工作位置", "手车试验位置", "闭锁备自投", "远方控制", "复归", "检修")

DO_PRT_TYPE_NAME = (
    "进线自投1跳1DL",
    "进线自投1合2DL",
    "进线自投2跳2DL",
    "进线自投2合1DL",
    "进线自复1跳2DL",
    "进线自复1合1DL",
    "进线自复2跳1DL",
    "进线自复2合2DL",
    "母联自投1跳1DL",
    "母联自投1合3DL",
    "母联自投2跳2DL",
    "母联自投2合3DL",
    "母联自复1跳3DL",
    "母联自复1合1DL",
    "母联自复2跳3DL",
    "母联自复2合2DL",
    "开关拒动",
    "母线I PT断线",
    "母线II PT断线",
    "1#进线PT断线",
    "2#进线PT断线",
    "遥控跳1DL",
    "遥控合1DL",
    "遥控跳2DL",
    "遥控合2DL",
    "遥控跳3DL",
    "遥控合3DL",
)

PRT_STATUS_TYPE_NAME = (
    "进线自投1充电完成",
    "进线自投1放电",
    "进线自投1启动",
    "进线自投1启动返回",
    "进线自投1跳1DL",
    "进线自投1 1DL拒跳",
    "进线自投1合2DL",
    "进线自投1 2DL拒合",
    "进线自投1动作成功",
    "进线自投2充电完成",
    "进线自投2放电",
    "进线自投2启动",
    "进线自投2启动返回",
    "进线自投2跳2DL",
    "进线自投2 2DL拒跳",
    "进线自投2合1DL",
    "进线自投2 1DL拒合",
    "进线自投2动作成功",
    "进线自复1充电完成",
    "进线自复1放电",
    "进线自复1启动",
    "进线自复1启动返回",
    "进线自复1跳2DL",
    "进线自复1 2DL拒跳",
    "进线自复1合1DL",
    "进线自复1 1DL拒合",
    "进线自复1动作成功",
    "进线自复2充电完成",
    "进线自复2放电",
    "进线自复2启动",
    "进线自复2启动返回",
    "进线自复2跳1DL",
    "进线自复2 1DL拒跳",
    "进线自复2合2DL",
    "进线自复2 2DL拒合",
    "进线自复2动作成功",
    "母线自投1充电完成",
    "母线自投1放电",
    "母线自投1启动",
    "母线自投1启动返回",
    "母线自投1跳1DL",
    "母线自投1 1DL拒跳",
    "母线自投1合3DL",
    "母线自投1 3DL拒合",
    "母线自投1动作成功",
    "母线自投2充电完成",
    "母线自投2放电",
    "母线自投2启动",
    "母线自投2启动返回",
    "母线自投2跳2DL",
    "母线自投2 2DL拒跳",
    "母线自投2合3DL",
    "母线自投2 3DL拒合",
    "母线自投2动作成功",
    "母线自复1充电完成",
    "母线自复1放电",
    "母线自复1启动",
    "母线自复1启动返回",
    "母线自复1跳3DL",
    "母线自复1 3DL拒跳",
    "母线自复1合1DL",
    "母线自复1 1DL拒合",
    "母线自复1动作成功",
    "母线自复2充电完成",
    "母线自复2放电",
    "母线自复2启动",
    "母线自复2启动返回",
    "母线自复2跳3DL",
    "母线自复2 3DL拒跳",
    "母线自复2合2DL",
    "母线自复2 2DL拒合",
    "母线自复2动作成功",
    "母线I PT断线",
    "母线II PT断线",
    "1#进线PT断线",
    "2#进线PT断线",
    "遥控合1DL",
    "遥控分1DL",
    "遥控合2DL",
    "遥控分2DL",
    "遥控合3DL",
    "遥控分3DL",
)

DI_NUM = 12
DO_NUM = 10
DO_NUM_WITHOUT_LED = 8
DO_NAME = ("开出1", "开出2", "开出3", "开出4", "开出5", "开出6", "开出7", "开出8", "动作灯", "告警灯")


PHASE_NAME = ("None", "Uab1", "Ubc1", "Uca1", "Uab2", "Ubc2", "Uca2", "Ix1", "Ux1", "Ix2", "Ux2")


def eventPtrActDecode(data: int):
    """保护事件动作值数据解析 int-->str"""
    unit = ""
    rate = 2
    # typePrt = data[0]
    phasePrt = data[2]
    valuePrt = data[3]
    specIdx = -1
    if phasePrt > 0:  # 有相别
        outName = PHASE_NAME[phasePrt]
        if phasePrt == 7:
            specIdx = 8
        else:
            specIdx = phasePrt - 1
        if phasePrt in (1, 2, 3, 7, 8):
            unit = "A"
        else:
            unit = "V"
    elif phasePrt == 0:  # 无相别
        outName = None
    if outName is None:
        return "无动作值", specIdx
    else:
        return "{}: {:.{}f}{}".format(outName, valuePrt / (10 ** rate), rate, unit), specIdx


def eventPtrDetailDecode(data: int):
    """保护事件特征值数据解析 int-->str"""
    newData = [d / 100 for d in data]
    units = ("V", "V", "V", "V", "V", "V", "A", "V", "A", "V")
    for d, unit in zip(newData, units):
        yield "{:.2f}{}".format(d, unit)


# 过流1段保护投退定值     3
# 过流2段保护投退定值     3
# 过流3段保护投退定值     3
# 反时限保护投退定值      5
# 零序1段保护投退定值     3
# 零序2段保护投退定值     3
# 负序过流保护投退定值    3
# 过负荷保护投退定值      3
# 低电压保护投退定值      3
# 过电压保护投退定值      3
# 不平衡电流保护投退定值  3
# 不平衡电压保护投退定值  3
# 充电保护投退定值        4
# 重合闸保护投退定值      2
# 后加速保护投退定值      3
# 电动机保护投退          3
# 过热保护投退定值        3
# 启动速断保护投退定值    3
# 启动超时保护投退定值    1
# 堵转保护投退定值        3
# 重瓦斯保护投退定值      2
# 轻瓦斯保护投退定值      2
# 高温段保护投退定值      2
# 超温保护投退定值        2
# 压力释放保投退定值      2
# 非电量1保护投退定值     2
# 非电量2保护投退定值     2
# PT断线保护投退定值      1


ProtectGroupConf = namedtuple("ProtectGroupConf", "offset name isCheckable groupPlace")
PTR_GROUP_CONFS = (
    ProtectGroupConf(-1, "公共参数1", False, (0, 0, 3, 1)),
    ProtectGroupConf(-1, "公共参数2", False, (0, 1, 3, 1)),
    ProtectGroupConf(11, "进线自投1", True, (0, 2, 3, 1)),
    ProtectGroupConf(17, "进线自投2", True, (0, 3, 3, 1)),
    ProtectGroupConf(23, "进线自复1", True, (0, 4, 3, 1)),
    ProtectGroupConf(27, "进线自复2", True, (3, 0, 3, 1)),
    ProtectGroupConf(31, "母线自投1", True, (3, 1, 3, 1)),
    ProtectGroupConf(35, "母线自投2", True, (3, 2, 3, 1)),
    ProtectGroupConf(39, "母线自复1", True, (3, 3, 3, 1)),
    ProtectGroupConf(43, "母线自复2", True, (3, 4, 3, 1)),
    ProtectGroupConf(47, "母线I  PT断线", True, (6, 0, 2, 1)),
    ProtectGroupConf(50, "母线II PT断线", True, (6, 1, 2, 1)),
    ProtectGroupConf(53, "#1进线 PT断线", True, (6, 2, 2, 1)),
    ProtectGroupConf(56, "#2进线 PT断线", True, (6, 3, 2, 1)),
)


ProtectSubWidgetConf = namedtuple("ProtectSubWidgetConf", "offset itemName range unitName groupIndex")
PTR_SUB_CONFS = (
    # 公共参数1
    ProtectSubWidgetConf(0, "母线PT投退状态", (0, 1), "", (0, 0)),
    ProtectSubWidgetConf(1, "进线自投无流定值", (0.05, 50.00), "A", (0, 1)),
    ProtectSubWidgetConf(2, "进线自投无压定值", (5.00, 456.00), "V", (0, 2)),
    ProtectSubWidgetConf(3, "进线自投有压定值", (5.00, 456.00), "V", (0, 3)),
    ProtectSubWidgetConf(4, "进线自复有压定值", (5.00, 456.00), "V", (0, 4)),
    # 公共参数2
    ProtectSubWidgetConf(5, "母联自投无流定值", (0.05, 50.00), "A", (1, 0)),
    ProtectSubWidgetConf(6, "母联自投无压定值", (5.00, 456.00), "V", (1, 1)),
    ProtectSubWidgetConf(7, "母联自投有压定值", (5.00, 456.00), "V", (1, 2)),
    ProtectSubWidgetConf(8, "母联自复有压定值", (5.00, 456.00), "V", (1, 3)),
    ProtectSubWidgetConf(9, "拒动判别时间定值", (0.00, 60.00), "S", (1, 4)),
    # 进线自投1
    ProtectSubWidgetConf(12, "开关分位检测", (0, 1), "", (2, 0)),
    ProtectSubWidgetConf(13, "进线1电压检测", (0, 1), "", (2, 1)),
    ProtectSubWidgetConf(14, "进线1电压检测", (0, 1), "", (2, 2)),
    ProtectSubWidgetConf(15, "时间定值", (0.00, 60.00), "S", (2, 3)),
    # 进线自投2
    ProtectSubWidgetConf(18, "开关分位检测", (0, 1), "", (3, 0)),
    ProtectSubWidgetConf(19, "进线1电压检测", (0, 1), "", (3, 1)),
    ProtectSubWidgetConf(20, "进线1电压检测", (0, 1), "", (3, 2)),
    ProtectSubWidgetConf(21, "时间定值", (0.00, 60.00), "S", (3, 3)),
    # 进线自复1
    ProtectSubWidgetConf(24, "开关分位检测", (0, 1), "", (4, 0)),
    ProtectSubWidgetConf(25, "时间定值", (0.00, 60.00), "S", (4, 1)),
    # 进线自复2
    ProtectSubWidgetConf(28, "开关分位检测", (0, 1), "", (5, 0)),
    ProtectSubWidgetConf(29, "时间定值", (0.00, 60.00), "S", (5, 1)),
    # 母线自投1
    ProtectSubWidgetConf(32, "开关分位检测", (0, 1), "", (6, 0)),
    ProtectSubWidgetConf(33, "时间定值", (0.00, 60.00), "S", (6, 1)),
    # 母线自投2
    ProtectSubWidgetConf(36, "开关分位检测", (0, 1), "", (7, 0)),
    ProtectSubWidgetConf(37, "时间定值", (0.00, 60.00), "S", (7, 1)),
    # 母线自复1
    ProtectSubWidgetConf(40, "开关分位检测", (0, 1), "", (8, 0)),
    ProtectSubWidgetConf(41, "时间定值", (0.00, 60.00), "S", (8, 1)),
    # 母线自复2
    ProtectSubWidgetConf(44, "开关分位检测", (0, 1), "", (9, 0)),
    ProtectSubWidgetConf(45, "时间定值", (0.00, 60.00), "S", (9, 1)),
    # 母线I PT断线
    ProtectSubWidgetConf(48, "母线I PT断线", (0.00, 99.99), "S", (10, 0)),
    # 母线II PT断线
    ProtectSubWidgetConf(51, "母线II PT断线", (0.00, 99.99), "S", (11, 0)),
    # #1进线 PT断线
    ProtectSubWidgetConf(54, "#1进线 PT断线", (0.00, 99.99), "S", (12, 0)),
    # #2进线 PT断线
    ProtectSubWidgetConf(57, "#2进线 PT断线", (0.00, 99.99), "S", (13, 0)),
)

PRT_ONOFF_NAMES = ("无选择", "1DL", "2DL", "3DL")
Mea_LN_NAMES = ("三相三线", "三相四线")

COMM_BAUD_LIST = ("1200", "2400", "4800", "9600", "19200")

PrtUI_Conf = namedtuple("PrtUI_Conf", "name format rate unit toleranceIndex")
PRT_UI_CONFS = (
    PrtUI_Conf("Uab1", "6.2f", 100, "V", 0),
    PrtUI_Conf("Ubc1", "6.2f", 100, "V", 1),
    PrtUI_Conf("Uca1", "6.2f", 100, "V", 2),
    PrtUI_Conf("Uab2", "6.2f", 100, "V", 6),
    PrtUI_Conf("Ubc2", "6.2f", 100, "V", 7),
    PrtUI_Conf("Uca2", "6.2f", 100, "V", 8),
    PrtUI_Conf("Ix1", "6.2f", 100, "A", 12),
    PrtUI_Conf("Ux1", "6.2f", 100, "V", 13),
    PrtUI_Conf("Ix2", "6.2f", 100, "A", 14),
    PrtUI_Conf("Ux2", "6.2f", 100, "V", 15),
    PrtUI_Conf("φ Uab1", "6.1f", 10, "°", 3),
    PrtUI_Conf("φ Ubc1", "6.1f", 10, "°", 4),
    PrtUI_Conf("φ Uca1", "6.1f", 10, "°", 5),
    PrtUI_Conf("φ Uab2", "6.1f", 10, "°", 9),
    PrtUI_Conf("φ Ubc2", "6.1f", 10, "°", 10),
    PrtUI_Conf("φ Uca2", "6.1f", 10, "°", 11),
)

BypassConf = namedtuple("BypassConf", "name range decimal singleStep unit modbusAddr checked")
BYPASS_CONFS = (
    BypassConf("线电压Uab1有效值", (0.00, 655.35), 2, 0.1, "V", 0x000E, True),
    BypassConf("线电压Uab1相角", (0.0, 360.0), 1, 0.1, "°", 0x0018, False),
    BypassConf("线电压Ubc1有效值", (0.00, 655.35), 2, 0.1, "V", 0x000F, True),
    BypassConf("线电压Uab1相角", (0.0, 360.0), 1, 0.1, "°", 0x0019, False),
    BypassConf("线电压Uca1有效值", (0.00, 655.35), 2, 0.1, "V", 0x0010, True),
    BypassConf("线电压Uca1相角", (0.0, 360.0), 1, 0.1, "°", 0x001A, False),
    BypassConf("线电压Uab2有效值", (0.00, 655.35), 2, 0.05, "V", 0x0011, True),
    BypassConf("线电压Uab2相角", (0.0, 360.0), 1, 0.1, "°", 0x001B, False),
    BypassConf("线电压Ubc2有效值", (0.00, 655.35), 2, 0.05, "V", 0x0012, True),
    BypassConf("线电压Uab2相角", (0.0, 360.0), 1, 0.1, "°", 0x001C, False),
    BypassConf("线电压Uca2有效值", (0.00, 655.35), 2, 0.05, "V", 0x0013, True),
    BypassConf("线电压Uca2相角", (0.0, 360.0), 1, 0.1, "°", 0x001D, False),
    BypassConf("电流Ix1有效值", (0.00, 655.35), 2, 0.1, "A", 0x0014, True),
    BypassConf("电压Ux1有效值", (0.00, 655.35), 2, 0.1, "V", 0x0015, True),
    BypassConf("电流Ix2有效值", (0.00, 655.35), 2, 0.1, "A", 0x0016, True),
    BypassConf("电压Ux2有效值", (0.00, 655.35), 2, 0.1, "V", 0x0017, True),
)

AUTO_ADUJST_NAME = ("电压校正", "Ix电流校正", "Ux电压校正")
AdjustValueConf = namedtuple("AdjustValueConf", "name range decimal singleStep")
ADJUST_VALUE_CONFS = (
    AdjustValueConf("UAB1幅值", (0.5, 1.5), 3, 0.01),
    AdjustValueConf("UAB1相角", (0.0, 360.0), 1, 1),
    AdjustValueConf("UBC1幅值", (0.5, 1.5), 3, 0.01),
    AdjustValueConf("UBC1相角", (0.0, 360.0), 1, 1),
    AdjustValueConf("UAB2幅值", (0.5, 1.5), 3, 0.01),
    AdjustValueConf("UAB2相角", (0.0, 360.0), 1, 1),
    AdjustValueConf("UBC2幅值", (0.5, 1.5), 3, 0.01),
    AdjustValueConf("UBC2相角", (0.0, 360.0), 1, 1),
    AdjustValueConf("Ix1幅值", (0.5, 1.5), 3, 0.01),
    AdjustValueConf("Ix1相角", (0.0, 360.0), 1, 1),
    AdjustValueConf("Ux1幅值", (0.5, 1.5), 3, 0.01),
    AdjustValueConf("Ux1相角", (0.0, 360.0), 1, 1),
    AdjustValueConf("Ix2幅值", (0.5, 1.5), 3, 0.01),
    AdjustValueConf("Ix2相角", (0.0, 360.0), 1, 1),
    AdjustValueConf("Ux2幅值", (0.5, 1.5), 3, 0.01),
    AdjustValueConf("Ux2相角", (0.0, 360.0), 1, 1),
)


FAULT_NAME_TYPE_1 = (
    "上电自检故障",
    "AD硬件故障",
    "Uab1电压通道故障",
    "Ubc1电压通道故障",
    "Uab2电压通道故障",
    "Ubc2电压通道故障",
    "Ix1电流通道故障",
    "Ux1电压通道故障",
    "Ix2电流通道故障",
    "Ux2电压通道故障",
    "系统时钟硬件错误",
    "片内FLASH读取错误",
    "片内FLASH写入错误",
    "片外EEPROM存储器读取错误",
    "片外EEPROM存储器写入错误",
    "以太网1硬件错误",
    "以太网1通信中断",
    "以太网2硬件错误",
    "以太网2通信中断",
    "RS485_1通信中断",
    "RS485_2通信中断",
    "备用",
    "备用",
    "时间错误码",
    "动态内存1不足",
    "动态内存2不足",
    "动态内存3不足",
    "自动校正失败",
)

FAULT_NAME_TYPE_2 = ("串口通信参数错误", "网口通信参数错误", "开入参数错误", "开出参数错误", "设备参数错误", "变比参数错误", "保护事件数据错误", "遥信事件数据错误")

FAULT_NAME_TYPE_3 = (
    "公共定值参数",
    "进线自投1参数",
    "进线自投2参数",
    "进线自复1参数",
    "进线自复2参数",
    "母线自投1参数",
    "母线自投2参数",
    "母线自复1参数",
    "母线自复2参数",
    "母线I PT断线参数",
    "母线II PT断线参数",
    "1#进线PT断线参数",
    "2#进线PT断线参数",
)

FAULT_NAME_TYPE_4 = (
    "进线自投1充电完成",
    "进线自投1放电",
    "进线自投1启动",
    "进线自投1启动返回",
    "进线自投1跳1DL",
    "进线自投1 1DL拒跳",
    "进线自投1合2DL",
    "进线自投1 2DL拒合",
    "进线自投1动作成功",
    "进线自投2充电完成",
    "进线自投2放电",
    "进线自投2启动",
    "进线自投2启动返回",
    "进线自投2跳2DL",
    "进线自投2 2DL拒跳",
    "进线自投2合1DL",
    "进线自投2 1DL拒合",
    "进线自投2动作成功",
    "进线自复1充电完成",
    "进线自复1放电",
    "进线自复1启动",
    "进线自复1启动返回",
    "进线自复1跳2DL",
    "进线自复1 2DL拒跳",
    "进线自复1合1DL",
    "进线自复1 1DL拒合",
    "进线自复1动作成功",
    "进线自复2充电完成",
    "进线自复2放电",
    "进线自复2启动",
    "进线自复2启动返回",
    "进线自复2跳1DL",
    "进线自复2 1DL拒跳",
    "进线自复2合2DL",
    "进线自复2 2DL拒合",
    "进线自复2动作成功",
    "母线自投1充电完成",
    "母线自投1放电",
    "母线自投1启动",
    "母线自投1启动返回",
    "母线自投1跳1DL",
    "母线自投1 1DL拒跳",
    "母线自投1合3DL",
    "母线自投1 3DL拒合",
    "母线自投1动作成功",
    "母线自投2充电完成",
    "母线自投2放电",
    "母线自投2启动",
    "母线自投2启动返回",
    "母线自投2跳2DL",
    "母线自投2 2DL拒跳",
    "母线自投2合3DL",
    "母线自投2 3DL拒合",
    "母线自投2动作成功",
    "母线自复1充电完成",
    "母线自复1放电",
    "母线自复1启动",
    "母线自复1启动返回",
    "母线自复1跳3DL",
    "母线自复1 3DL拒跳",
    "母线自复1合1DL",
    "母线自复1 1DL拒合",
    "母线自复1动作成功",
    "母线自复2充电完成",
    "母线自复2放电",
    "母线自复2启动",
    "母线自复2启动返回",
    "母线自复2跳3DL",
    "母线自复2 3DL拒跳",
    "母线自复2合2DL",
    "母线自复2 2DL拒合",
    "母线自复2动作成功",
    "母线I PT断线",
    "母线II PT断线",
    "1#进线PT断线",
    "2#进线PT断线",
    "遥控合1DL",
    "遥控分1DL",
    "遥控合2DL",
    "遥控分2DL",
    "遥控合3DL",
    "遥控分3DL",
)

FAULT_NAME = (FAULT_NAME_TYPE_1, FAULT_NAME_TYPE_2, FAULT_NAME_TYPE_3, FAULT_NAME_TYPE_4)
FAULT_GROUP_NAME = ("硬件故障", "系统参数", "保护参数", "保护动作")


ReportMeasureConf = namedtuple("MeasureConf", "name unit rate format dataAddress toleranceType signed")
REPORT_PRT_CONFS = (
    ReportMeasureConf("Uab1", "V", 100, "6.2f", 0x000E, "percent", False),
    ReportMeasureConf("Ubc1", "V", 100, "6.2f", 0x000F, "percent", False),
    ReportMeasureConf("Uca1", "V", 100, "6.2f", 0x0010, "percent", False),
    ReportMeasureConf("Uab2", "V", 100, "6.2f", 0x0011, "percent", False),
    ReportMeasureConf("Ubc2", "V", 100, "6.2f", 0x0012, "percent", False),
    ReportMeasureConf("Uca2", "V", 100, "6.2f", 0x0013, "percent", False),
    ReportMeasureConf("Ix1", "A", 100, "6.2f", 0x0014, "percent", False),
    ReportMeasureConf("UX1", "V", 100, "6.2f", 0x0015, "percent", False),
    ReportMeasureConf("Ix2", "A", 100, "6.2f", 0x0016, "percent", False),
    ReportMeasureConf("UX2", "V", 100, "6.2f", 0x0017, "percent", False),
)

ReportDataTableConf = namedtuple("ReportDataTableConf", "rowNames columnNames")
ReportDataTableItemConf = namedtuple("ReportDataTableItemConf", "row column keyName manten goukaku")

REPORT_PRT_TABLE_CONF = ReportDataTableConf(
    ("1", "2", "3", "4"), ("Uab1", "Ubc1", "Uca1", "Uab2", "Ubc2", "Uca2", "Ix1", "Ux1", "Ix2", "Ux2")
)

REPORT_PRT_TABLE_ITEM_CONF = (
    # Uab 1
    ReportDataTableItemConf(0, 0, "prt_uab1_1", 30, (0.98, 1.02)),
    ReportDataTableItemConf(1, 0, "prt_uab1_2", 120, (0.98, 1.02)),
    ReportDataTableItemConf(2, 0, "prt_uab1_3", 160, (0.98, 1.02)),
    ReportDataTableItemConf(3, 0, "prt_uab1_4", 450, (0.98, 1.02)),
    ReportDataTableItemConf(0, 1, "prt_ubc1_1", 30, (0.98, 1.02)),
    ReportDataTableItemConf(1, 1, "prt_ubc1_2", 120, (0.98, 1.02)),
    ReportDataTableItemConf(2, 1, "prt_ubc1_3", 160, (0.98, 1.02)),
    ReportDataTableItemConf(3, 1, "prt_ubc1_4", 450, (0.98, 1.02)),
    ReportDataTableItemConf(0, 2, "prt_uca1_1", 30, (0.98, 1.02)),
    ReportDataTableItemConf(1, 2, "prt_uca1_2", 120, (0.98, 1.02)),
    ReportDataTableItemConf(2, 2, "prt_uca1_3", 160, (0.98, 1.02)),
    ReportDataTableItemConf(3, 2, "prt_uca1_4", 450, (0.98, 1.02)),
    # Uab 2
    ReportDataTableItemConf(0, 3, "prt_uab2_1", 30, (0.98, 1.02)),
    ReportDataTableItemConf(1, 3, "prt_uab2_2", 120, (0.98, 1.02)),
    ReportDataTableItemConf(2, 3, "prt_uab2_3", 160, (0.98, 1.02)),
    ReportDataTableItemConf(3, 3, "prt_uab2_4", 450, (0.98, 1.02)),
    ReportDataTableItemConf(0, 4, "prt_ubc2_1", 30, (0.98, 1.02)),
    ReportDataTableItemConf(1, 4, "prt_ubc2_2", 120, (0.98, 1.02)),
    ReportDataTableItemConf(2, 4, "prt_ubc2_3", 160, (0.98, 1.02)),
    ReportDataTableItemConf(3, 4, "prt_ubc2_4", 450, (0.98, 1.02)),
    ReportDataTableItemConf(0, 5, "prt_uca2_1", 30, (0.98, 1.02)),
    ReportDataTableItemConf(1, 5, "prt_uca2_2", 120, (0.98, 1.02)),
    ReportDataTableItemConf(2, 5, "prt_uca2_3", 160, (0.98, 1.02)),
    ReportDataTableItemConf(3, 5, "prt_uca2_4", 450, (0.98, 1.02)),
    # Ix Ux 1
    ReportDataTableItemConf(0, 6, "prt_ix1_1", 0.5, (0.98, 1.02)),
    ReportDataTableItemConf(1, 6, "prt_ix1_2", 8, (0.98, 1.02)),
    ReportDataTableItemConf(2, 6, "prt_ix1_3", 12, (0.98, 1.02)),
    ReportDataTableItemConf(3, 6, "prt_ix1_4", 20, (0.98, 1.02)),
    ReportDataTableItemConf(0, 7, "prt_ux1_1", 30, (0.98, 1.02)),
    ReportDataTableItemConf(1, 7, "prt_ux1_2", 120, (0.98, 1.02)),
    ReportDataTableItemConf(2, 7, "prt_ux1_3", 160, (0.98, 1.02)),
    ReportDataTableItemConf(3, 7, "prt_ux1_4", 450, (0.98, 1.02)),
    # Ix Ux 2
    ReportDataTableItemConf(0, 8, "prt_ix2_1", 0.5, (0.98, 1.02)),
    ReportDataTableItemConf(1, 8, "prt_ix2_2", 8, (0.98, 1.02)),
    ReportDataTableItemConf(2, 8, "prt_ix2_3", 12, (0.98, 1.02)),
    ReportDataTableItemConf(3, 8, "prt_ix2_4", 20, (0.98, 1.02)),
    ReportDataTableItemConf(0, 9, "prt_ux2_1", 30, (0.98, 1.02)),
    ReportDataTableItemConf(1, 9, "prt_ux2_2", 120, (0.98, 1.02)),
    ReportDataTableItemConf(2, 9, "prt_ux2_3", 160, (0.98, 1.02)),
    ReportDataTableItemConf(3, 9, "prt_ux2_4", 450, (0.98, 1.02)),
)

REPORT_PRT_TABLE_CONF_FILE_PATH = "./config/spac800_bt_prt_table.json"
if os.path.exists(REPORT_PRT_TABLE_CONF_FILE_PATH) and os.path.isfile(REPORT_PRT_TABLE_CONF_FILE_PATH):
    try:
        with open(REPORT_PRT_TABLE_CONF_FILE_PATH, "r") as f:
            dd = json.load(f)
        REPORT_PRT_TABLE_ITEM_CONF = [ReportDataTableItemConf(**i) for i in dd]
    except Exception:
        print(stackprinter.format())
else:
    try:
        basedir = os.path.dirname(os.path.abspath(REPORT_PRT_TABLE_CONF_FILE_PATH))
        os.makedirs(basedir, exist_ok=True)
        with open(REPORT_PRT_TABLE_CONF_FILE_PATH, "w", encoding="utf-8") as f:
            json.dump([ob._asdict() for ob in REPORT_PRT_TABLE_ITEM_CONF], f, ensure_ascii=False, indent=4)
    except Exception:
        print(stackprinter.format())

# 保护输出组
ROOT_3 = 3 ** 0.5
AUTO_TEST_OUTPUT_PRT = (
    # U I
    (
        AutoTestUpdateConf(
            1, ("uab1_4", "ubc1_4", "uca1_4", "uab2_4", "ubc2_4", "uca2_4", "ux1_4", "ux2_4", "ix1_1", "ix2_1")
        ),
        KS833_OutputStatus(450 / ROOT_3, 450 / ROOT_3, 450 / ROOT_3, 0, 240, 120, 0.5, 0.5, 0, 0, 240, 120),
    ),
    (
        AutoTestUpdateConf(
            1, ("uab1_3", "ubc1_3", "uca1_3", "uab2_3", "ubc2_3", "uca2_3", "ux1_3", "ux2_3", "ix1_2", "ix2_2")
        ),
        KS833_OutputStatus(160 / ROOT_3, 160 / ROOT_3, 160 / ROOT_3, 0, 240, 120, 8, 8, 0, 0, 240, 120),
    ),
    (
        AutoTestUpdateConf(
            1, ("uab1_2", "ubc1_2", "uca1_2", "uab2_2", "ubc2_2", "uca2_2", "ux1_2", "ux2_2", "ix1_3", "ix2_3")
        ),
        KS833_OutputStatus(120 / ROOT_3, 120 / ROOT_3, 120 / ROOT_3, 0, 240, 120, 12, 12, 0, 0, 240, 120),
    ),
    (
        AutoTestUpdateConf(
            1, ("uab1_1", "ubc1_1", "uca1_1", "uab2_1", "ubc2_1", "uca2_1", "ux1_1", "ux2_1", "ix1_4", "ix2_4")
        ),
        KS833_OutputStatus(30 / ROOT_3, 30 / ROOT_3, 30 / ROOT_3, 0, 240, 120, 20, 20, 0, 0, 240, 120),
    ),
)
AUTO_TEST_OUTPUT_PRT_FILE_PATH = "./config/spac800_bt_prt_auto_test_output.json"
if os.path.exists(AUTO_TEST_OUTPUT_PRT_FILE_PATH) and os.path.isfile(AUTO_TEST_OUTPUT_PRT_FILE_PATH):
    try:
        with open(AUTO_TEST_OUTPUT_PRT_FILE_PATH, "r") as f:
            dd = json.load(f)
        AUTO_TEST_OUTPUT_PRT = [(AutoTestUpdateConf(**i), KS833_OutputStatus(**j)) for i, j in dd]
    except Exception:
        print(stackprinter.format())
else:
    try:
        basedir = os.path.dirname(os.path.abspath(AUTO_TEST_OUTPUT_PRT_FILE_PATH))
        os.makedirs(basedir, exist_ok=True)
        with open(AUTO_TEST_OUTPUT_PRT_FILE_PATH, "w", encoding="utf-8") as f:
            json.dump(
                [(ob._asdict(), oc._asdict()) for ob, oc in AUTO_TEST_OUTPUT_PRT], f, ensure_ascii=False, indent=4
            )
    except Exception:
        print(stackprinter.format())


def getWriteAreaName(addr: int):
    """get write area name"""
    if addr == 0x0000:
        return "日期时间"
    elif addr == 0x0001:
        return "月日"
    elif addr == 0x0002:
        return "时分"
    elif addr == 0x0003:
        return "毫秒"
    elif addr == 0x0001:
        return "月日"
    elif addr in range(0x0040, 0x0043 + 1):
        return "有功电度"
    elif addr in range(0x0048, 0x004B + 1):
        return "无功电度"
    elif addr in range(0x0200, 0x0204 + 1):
        return "系统参数->保护参数"
    elif addr in range(0x0205, 0x0207 + 1):
        return "系统参数->测量参数"
    elif addr in range(0x0208, 0x0209 + 1):
        return "系统参数->显示参数"
    elif addr in range(0x0300, 0x030C + 1):
        return "开入参数->遥信去抖时间"
    elif addr in range(0x030D, 0x0319 + 1):
        return "开入参数->开入功能选择"
    elif addr in range(0x0400, 0x0409 + 1):
        return "出口参数->开出时间"
    elif addr in range(0x040A, 0x0428 + 1):
        return "出口参数->保护出口选择"
    elif addr in range(0x0600, 0x0602 + 1):
        return "通讯参数->串口1"
    elif addr in range(0x0603, 0x0605 + 1):
        return "通讯参数->串口2"
    elif addr in range(0x0606, 0x0612 + 1):
        return "通讯参数->网口1"
    elif addr in range(0x0613, 0x061F + 1):
        return "通讯参数->网口2"
    elif addr in range(0x0700, 0x0707 + 1):
        return "产品信息参数"
    elif addr in range(0x0800, 0x084B + 1):
        return "保护参数"
    elif addr == 0x0B00:
        return "遥控跳1DL"
    elif addr == 0x0B01:
        return "遥控合1DL"
    elif addr == 0x0B02:
        return "遥控跳2DL"
    elif addr == 0x0B03:
        return "遥控合2DL"
    elif addr == 0x0B04:
        return "遥控跳3DL"
    elif addr == 0x0B05:
        return "遥控合3DL"
    elif addr == 0x0B20:
        return "信号复归"
    elif addr == 0x0B21:
        return "电度清零"
    elif addr == 0x0B22:
        return "保护事件记录清零"
    elif addr == 0x0B23:
        return "遥信事件记录清零"
    elif addr == 0x0B24:
        return "故障录波记录清零"
    elif addr == 0x0E20:
        return "保护校正操作"
    elif addr == 0x0E21:
        return "恢复出厂设置"
    elif addr == 0x0E22:
        return "运行模式切换"
    elif addr in range(0x0E40, 0x0E4F + 1):
        return "低量程校正系数"
    elif addr in range(0x0E50, 0x0E5F + 1):
        return "高量程校正系数"
    elif addr in range(0x0E70, 0x0E77 + 1):
        return "开出 {} 遥控操作".format(addr - 0x0E70 + 1)
    elif addr == 0x0E90:
        return "交流旁路通道控制"
    elif addr in range(0x0E91, 0x0EA1 + 1):
        return "交流旁路值"
    else:
        return ""


PRT_AREA_NAMES = (
    "母线PT投退定值",
    "进线自投无流定值",
    "进线自投无压定值",
    "进线自投有压定值",
    "进线自复有压定值",
    "母线自投无流定值",
    "母线自投无压定值",
    "母线自投有压定值",
    "母线自复有压定值",
    "开关拒动时间定值",
    "备用",
    "进线自投1 方式投退定值",
    "进线自投1 开关分位检测投退定值",
    "进线自投1 #1进线电压检测投退定值",
    "进线自投1 #2进线电压检测",
    "进线自投1 自投延时定值",
    "备用",
    "进线自投2 方式投退定值",
    "进线自投2 开关分位检测投退定值",
    "进线自投2 #1进线电压检测投退定值",
    "进线自投2 #2进线电压检测",
    "进线自投2 自投延时定值",
    "备用",
    "进线自复1 方式投退定值",
    "进线自投1 开关分位检测投退定值",
    "进线自复1 自复延时定值",
    "备用",
    "进线自复2 方式投退定值",
    "进线自投2 开关分位检测投退定值",
    "进线自复2 自复延时定值",
    "备用",
    "母联自投1 方式投退定值",
    "母联自投1 开关分位检测投退定值",
    "母联自投1 自投延时",
    "备用",
    "母联自投2 方式投退定值",
    "母联自投2 开关分位检测投退定值",
    "母联自投2 自投延时定值",
    "备用",
    "母联自复1 方式投退定值",
    "母联自复1 开关分位检测投退定值",
    "母联自复1 自复延时定值",
    "备用",
    "母联自复2 方式投退定值",
    "母联自复2 开关分位检测投退定值",
    "母联自复2 自复延时定值",
    "备用",
    "母线I PT断线投退定值",
    "母线I PT断线告警时间定值",
    "备用",
    "母线II PT断线投退定值",
    "母线II PT断线告警时间定值",
    "备用",
    "1#进线PT断线投退定值",
    "1#进线PT断线告警时间定值",
    "备用",
    "2#进线PT断线投退定值",
    "2#进线PT断线告警时间定值",
    "备用",
)


def getPrtAreaName(addr: int):
    """get prt para addr name"""
    if addr >= 0 and addr < len(PRT_AREA_NAMES):
        return PRT_AREA_NAMES[addr]
    else:
        return "PRT_AREA_NAMES Out Range {}".format(addr)


HomeDispSelect = namedtuple("HomeDispSelect", ("offset name enable"))
HOME_DISP_ITEMS = (
    HomeDispSelect(0, "一次/二次值选择", True),
    HomeDispSelect(1, "Uab1线电压值", True),
    HomeDispSelect(2, "Ubc1线电压值", True),
    HomeDispSelect(3, "Uca1线电压值", True),
    HomeDispSelect(4, "Uab2线电压值", True),
    HomeDispSelect(5, "Ubc2线电压值", True),
    HomeDispSelect(6, "Uca2线电压值", True),
    HomeDispSelect(7, "#1进线电流值Ix1", True),
    HomeDispSelect(8, "#1进线电压值Ux1", True),
    HomeDispSelect(9, "#2进线电流值Ix2", True),
    HomeDispSelect(10, "#2进线电压值Ux2", True),
    HomeDispSelect(11, "Uab1线电压角度", True),
    HomeDispSelect(12, "Ubc1线电压角度", True),
    HomeDispSelect(13, "Uca1线电压角度", True),
    HomeDispSelect(14, "Uab2线电压角度", True),
    HomeDispSelect(15, "Ubc2线电压角度", True),
    HomeDispSelect(16, "Uca2线电压角度", True),
)
